/**************************************************************************************
*
*	Copyright (C) 2009 - 2012 Brent Andrew O. Chua - See LICENSE.txt for more info.
*		
*	Filename	-	Actor.h
*
*	Description -	Actors also acts as transform nodes. This naturally fits in our 
*					scene (transform) graph.
*
*	Comments	-
*
*	Modification History:
*			Name			   Date					Description
*			MrCodeSushi	-	07/31/2012	-	Creation of this file
**************************************************************************************/
#ifndef _PSX_ACTOR_H_
#define _PSX_ACTOR_H_

#include "PulseSTD.h"
#include "Vector3.h"
#include "Matrix4x4.h"
#include "SmartPointer.h"
#include "ParameterContainer.h"
#include "ComponentContainer.h"
#include "ComponentTransform.h"
#include "EventSystem.h"
#include "XMLParser.h"

namespace Pulse
{
	class Actor
	{
	public:

		~Actor( void );

		const ActorID GetID( void ) const { return m_ID; }

		Actor *GetParent( void ) { return m_pParent; }

		void SetEnabled( BOOL bEnable ) { m_bEnabled = bEnable; }

		BOOL IsEnabled( void ) const;

		void ToggleEnabled( void );

		void Update( FLOAT dt );

		void Render( Scene *pScene, ComponentCamera *pCamera, RenderView *pView );

	public:

		const Scene * GetScene( void ) const { return m_pScene; }
		
		Scene * GetScene( void ) { return m_pScene; }

		void SetPosition( const Vector3 *pPos );

		void SetPosition( FLOAT x, FLOAT y, FLOAT z );

		void SetRotation( FLOAT x, FLOAT y, FLOAT z );

		// Euler angles x - pitch, y - yaw, z - roll
		void SetRotation( const Vector3 *pRot );

		void SetRotationX( FLOAT angle );

		void SetRotationY( FLOAT angle );

		void SetRotationZ( FLOAT angle );

		Vector3 * GetRotation( void );
 
		void SetScale( const FLOAT scale );

		void SetScale( const Vector3 *pScale );

		Vector3 * GetScale( void );

		Matrix4x4 * GetTransformWorld( void );

		const Matrix4x4 * Actor::GetTransformWorld( void ) const;

		Matrix4x4 * GetTransformLocal( void );

		const Matrix4x4 * GetTransformLocal( void ) const;

		void SetTransformLocal( const Matrix4x4 *pTransform );

		void SetTransformWorld( const Matrix4x4 *pTransform );

		void SetUpdateLocalTransform( BOOL bUpdate = TRUE );

		void SetUpdateWorldTransform( BOOL bUpdate = TRUE );

	public:

		// To be called before rendering
		void BindRenderParameters( void );

		void InvalidateTransforms( void );
	
	public:

		IComponent * AddComponent( EComponent::Type type, XMLElementPtr pElem = PSX_NULL );

		IComponent * GetComponent( EComponent::Type componentType );

		IComponent * GetComponent( const CHAR *pName );
		
	public:

		// this, class member function
		// NOTE: WARNING! Make sure the func method accepts an EventPtr parameter. NOT IEvent*!
		template < typename ThisObject, typename Function >
		BOOL AddListener( const EEvent::Type event, ThisObject thisObject, Function func );

		// this, class member function
		// NOTE: WARNING! Make sure the func method accepts an EventPtr parameter. NOT IEvent*!
		template < typename ThisObject, typename Function >
		BOOL RemoveListener( const EEvent::Type event, ThisObject &thisObject, Function func );

		void ExecuteEvent( IEvent *pEvent );

		void PostEvent( EventPtr pEvent );

	public:

		SIZE_T GetNumChildren( void ) { return m_children.GetSize(); }

		INDEX_T AttachChild( ActorPtr pChild );

	public:

		// NOTE: Utility function to destroy an actor (not necessarily this actor)
		void Destroy( Actor *pActor );

		// Destroy self
		void Destroy( void );

		void Destroy( IComponent *pComponent );
		
	private:

		// Utility functions

		void PSX_NO_DIRECT_CALL RemoveAllComponents( void );

		const BOOL PSX_NO_DIRECT_CALL RemoveComponent( EComponent::Type componentType );

		void DetachChild( const Actor *pActor );

		ActorPtr GetChild( UINT index );

		ActorPtr GetChild( const Actor *pActor );

		void DetachAllChildren( void );

	private:

		// The render function above calls this function to avoid constant creation of the event
		void Render( EventRender *pEventRender );

		void SetScene( Scene *pScene ) { m_pScene = pScene; }
	
	private:

		friend FactoryActor;
		friend Scene;

		Actor( const ActorID id );

		// TODO: We might need a 3 step initialization process. PreInitialize, Initialize and PostInitialize
		EErrorCode::Type Initialize( XMLElementPtr pElem );

	private:

		typedef Array<ActorPtr> ChildList;

		const ActorID		m_ID;
		BOOL				m_bEnabled;
		Actor				*m_pParent;		// Yes, a naked pointer.
		ParameterContainer	m_params;		// NOTE: This may not be needed.
		ComponentContainer	m_components;
		ComponentTransform	*m_pTransform;	// This is needed in all SceneObjects so its built-in
		EventSystem			m_eventSystem;
		ChildList			m_children;
		Scene				*m_pScene;		// The scene it belongs to.
	};

	PSX_INLINE void Actor::SetPosition( const Vector3 *pPos )
	{
		m_pTransform->SetPosition( pPos );
	}

	PSX_INLINE void Actor::SetPosition( FLOAT x, FLOAT y, FLOAT z )
	{
		m_pTransform->SetPosition( x, y, z );
	}

	PSX_INLINE void Actor::SetRotation( FLOAT x, FLOAT y, FLOAT z )
	{
		m_pTransform->SetRotation( x, y, z );
	}

	// Euler angles x - pitch, y - yaw, z - roll
	PSX_INLINE void Actor::SetRotation( const Vector3 *pRot ) 
	{ 
		m_pTransform->SetRotation( pRot );
	}

	PSX_INLINE void Actor::SetRotationX( FLOAT angle ) 
	{ 
		m_pTransform->SetRotationX( angle );
	}

	PSX_INLINE void Actor::SetRotationY( FLOAT angle ) 
	{ 
		m_pTransform->SetRotationY( angle );
	}

	PSX_INLINE void Actor::SetRotationZ( FLOAT angle ) 
	{ 
		m_pTransform->SetRotationZ( angle );
	}

	PSX_INLINE Vector3 * Actor::GetRotation( void ) 
	{ 
		return m_pTransform->GetRotation();
	}

	PSX_INLINE void Actor::SetScale( const FLOAT scale )
	{
		m_pTransform->SetScale( scale );
	}

	PSX_INLINE void Actor::SetScale( const Vector3 *pScale )
	{
		m_pTransform->SetScale( pScale );
	}
		
	PSX_INLINE Vector3 * Actor::GetScale( void )
	{
		return m_pTransform->GetScale();
	}

	PSX_INLINE Matrix4x4 * Actor::GetTransformWorld( void )
	{
		return m_pTransform->GetTransformWorld();
	}

	PSX_INLINE const Matrix4x4 * Actor::GetTransformWorld( void ) const
	{
		return m_pTransform->GetTransformWorld();
	}

	PSX_INLINE Matrix4x4 * Actor::GetTransformLocal( void )
	{
		return m_pTransform->GetTransformLocal();
	}

	PSX_INLINE const Matrix4x4 * Actor::GetTransformLocal( void ) const
	{
		return m_pTransform->GetTransformLocal();
	}

	PSX_INLINE void Actor::SetTransformLocal( const Matrix4x4 *pTransform )
	{
		m_pTransform->SetTransformLocal( pTransform );
	}

	PSX_INLINE void Actor::SetTransformWorld( const Matrix4x4 *pTransform )
	{
		m_pTransform->SetTransformWorld( pTransform );
	}

	// this, class member function
	template < typename ThisObject, typename Function >
	BOOL Actor::AddListener( const EEvent::Type event, ThisObject thisObject, Function func )
	{
		return m_eventSystem.AddListener< ThisObject, Function>( event, thisObject, func );
	}

	// this, class member function
	template < typename ThisObject, typename Function >
	BOOL Actor::RemoveListener( const EEvent::Type event, ThisObject &thisObject, Function func )
	{
		return m_eventSystem.RemoveListener( event, thisObject, func );
	}

	PSX_INLINE void Actor::ExecuteEvent( IEvent *pEvent )
	{
		m_eventSystem.TriggerEvent( pEvent );
	}

	PSX_INLINE void Actor::PostEvent( EventPtr pEvent )
	{
		m_eventSystem.PostEvent( pEvent );
	}

	PSX_INLINE IComponent * Actor::GetComponent( EComponent::Type componentType )
	{
		return m_components.GetComponent( componentType );
	}

	PSX_INLINE IComponent * Actor::GetComponent( const CHAR *pName )
	{
		return m_components.GetComponent( pName );
	}

	PSX_INLINE const BOOL Actor::RemoveComponent( EComponent::Type componentType )
	{
		return m_components.RemoveComponent( componentType );
	}


}

#endif /* _PSX_ACTOR_H_ */